﻿using Application.DTO.User;
using Application.DTO;
using Application.IService.User;
using Domain.Entity.User;
using Infrastructure.IRepository;
using Infrastructure.IRepository.User;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using Infrastructure.Repository.User;
using System.Security.Cryptography;
using MD5Hash;
using Infrastructure.IRepository.Role;
using User_PageDTO = Application.DTO.User.User_PageDTO;
using System.Security.Claims;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using JWT;
using JWT.Serializers;
using JWT.Algorithms;
using System.IdentityModel.Tokens.Jwt;
using JWT.Exceptions;
using Infrastructure.Common.JWT;

namespace Application.Service.User
{
    public class UserService : BaseService<UserInfo>, IUserService
    {
        CSRedis.CSRedisClient redis = new CSRedis.CSRedisClient("127.0.0.1:6379");
        private readonly IUnitOfWork unitOfWork;
        private readonly IUserRepository userRepository;
        private readonly IRoleRepository roleRepository;
        private readonly IMapper mapper;
        private readonly IRoleUsersRepository roleUsersRepository;
        private readonly JwtHelper jwtHelper;
        private readonly JwtTokenOption _options;
        public UserService(IUnitOfWork unitOfWork, IUserRepository userRepository, IMapper mapper, IRoleUsersRepository roleUsersRepository, IRoleRepository roleRepository, JwtHelper jwtHelper, JwtTokenOption options) : base(unitOfWork, userRepository)
        {
            this.unitOfWork = unitOfWork;
            this.userRepository = userRepository;
            this.mapper = mapper;
            this.roleUsersRepository = roleUsersRepository;
            this.roleRepository = roleRepository;
            this.jwtHelper = jwtHelper;
            _options = options;
        }
        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="userName">登录名</param>
        /// <param name="password">密码</param>
        /// <returns></returns>
        public async Task<ResultDTO<LoginSuccessResponseDTO>> UserLogin(string userName, string password)
        {
            //对输入的密码进行MD5加密
            password = password.GetMD5();
            //根据登录名称获取用户信息
            var user = await userRepository.GetUserbyLoginName(userName);

            //判断是否为空
            if (user == null)
            {
                return new ResultDTO<LoginSuccessResponseDTO>(null, Domain.Enum.ResultStatusCode.ENTITYNULL, "不存在该用户！！！");
            }

            //判断该用户是否被禁用，若被禁用，则登录失败
            if (user.Status == 1)
            {
                return new ResultDTO<LoginSuccessResponseDTO>(null, Domain.Enum.ResultStatusCode.HANDLEFAIL, "该账号已被禁用！！！");
            }

            //判断密码
            if (password != user.Password)
            {
                //密码不一致，登录失败
                return new ResultDTO<LoginSuccessResponseDTO>(null, Domain.Enum.ResultStatusCode.PARAMSERROR, "密码错误");
            }
            //自定义荷载信息
            var clims = new List<Claim>();
            clims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
            clims.Add(new Claim("Id", user.Id.ToString()));
            clims.Add(new Claim("LoginName", user.LoginName));
            clims.Add(new Claim("Phone", user.Phone == null ? "空数据" : user.Phone));
            clims.Add(new Claim("Email", user.Email == null ? "空数据" : user.Email));
            //登录成功返回的DTO
            LoginSuccessResponseDTO info = mapper.Map<LoginSuccessResponseDTO>(user);
            //jwt令牌
            info.TokenInfo = jwtHelper.CreateTokenString(clims);
            //把令牌放入Redis，并添加过期时间(15分钟) 
            redis.Set(user.LoginName, info.TokenInfo.TokenStr);
            redis.Expire(user.LoginName, 60 * 90);
            //密码一致，登录成功
            return new ResultDTO<LoginSuccessResponseDTO>(
                info,
                Domain.Enum.ResultStatusCode.SUCCESS,
                "登录成功！！！"
                );
        }


        /// <summary>
        /// 注册账户
        /// </summary>
        /// <param name="dto">注册信息</param>
        /// <returns></returns>
        public async Task<ResultDTO<LoginSuccessResponseDTO>> RegisterAmount(RegisterAmountRequestDTO dto)
        {
            //对密码进行MD5加密
            dto.Password = dto.Password.GetMD5();

            //判断用户名是否重复
            var temp = await userRepository.GetUserbyLoginName(dto.LoginName);
            if (temp != null)
            {
                return new ResultDTO<LoginSuccessResponseDTO>(null, Domain.Enum.ResultStatusCode.HANDLEFAIL, "用户名已存在");
            }

            //转换为UserInfo所需的格式
            var user = mapper.Map<UserInfo>(dto);
            user.CreateTime = DateTime.Now;
            user.NickName = "系统用户" + Guid.NewGuid().ToString().Substring(0, 4);

            using (var tran = unitOfWork.GetDbContext().Database.BeginTransaction())
            {
                try
                {
                    //添加用户
                    await userRepository.Insert(user);

                    //保存数据
                    int h = await unitOfWork.SaveChanges();

                    //实例化用户角色关系 默认为普通账户
                    RoleUsers roleUsers = new RoleUsers
                    {
                        RoleId = 3,
                        UserId = user.Id,
                        CreateUserId = user.Id.ToString(),
                        CreateTime = DateTime.Now
                    };

                    //添加用户与角色关系
                    await roleUsersRepository.Insert(roleUsers);

                    //保存数据
                    int h2 = await unitOfWork.SaveChanges();

                    //返回结果
                    tran.Commit();
                    return new ResultDTO<LoginSuccessResponseDTO>(mapper.Map<LoginSuccessResponseDTO>(user), Domain.Enum.ResultStatusCode.SUCCESS, "注册成功");
                }
                catch (Exception ex)
                {
                    //发生异常，回滚事务
                    tran.Rollback();
                    return new ResultDTO<LoginSuccessResponseDTO>(null, Domain.Enum.ResultStatusCode.HANDLEFAIL, $"操作失败，错误信息为:{ex.Message}");
                }
            }
        }


        /// <summary>
        /// 找回密码，修改密码
        /// </summary>
        /// <param name="dto">用户名和密码信息</param>
        /// <returns></returns>
        public async Task<ResultDTO<int>> UpdatePassword(LoginRequestDTO dto)
        {
            //根据用户名查询用户信息
            var user = await userRepository.GetUserbyLoginName(dto.UserName);

            //判断用户是否存在
            if (user == null)
            {
                return new ResultDTO<int>(0, Domain.Enum.ResultStatusCode.ENTITYNULL, "不存在该用户！！！");
            }

            //修改密码，并进行MD5加密
            user.Password = dto.Password.GetMD5();
            await userRepository.Update(user);

            //保存数据
            int h = await unitOfWork.SaveChanges();

            //返回结果
            if (h > 0)
            {
                return new ResultDTO<int>(1, Domain.Enum.ResultStatusCode.SUCCESS, "修改成功");
            }
            else
            {
                return new ResultDTO<int>(-1, Domain.Enum.ResultStatusCode.HANDLEFAIL, "修改失败");
            }
        }


        /// <summary>
        /// 用户全部显示及分页查询
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<ResultDTO<PageDTO<User_PageDTO>>> Page(string UserName, int Pagesize, int Pageindex, int DepartmentId = -1)
        {
            try
            {
                //将角色表和用户角色表联查
                var rolelist = from a in await roleRepository.GetAll()
                               join b in await roleUsersRepository.GetAll() on
                               a.Id equals b.RoleId
                               select new
                               {
                                   a.Name,//角色名称
                                   b.UserId,//用户ID
                                   a.Id
                               };
                //查出所有的用户
                var data = from a in await userRepository.GetAll()
                           where a.IsDeleted == false
                           select new User_PageDTO
                           {
                               Id = a.Id,
                               Email = a.Email,
                               Icon = a.Icon,
                               LoginName = a.LoginName,
                               NickName = a.NickName,
                               Phone = a.Phone,
                               Status = a.Status,
                               DepartmentId=a.DepartmentId,
                               //通过用户ID找角色名称
                               Role = rolelist.Where(x => x.UserId == a.Id).Select(x => new Roles { RoleId = x.Id, RoleName = x.Name }).ToList()
                           };
                //用户昵称模糊查询
                if (!string.IsNullOrEmpty(UserName))
                {
                    data = data.Where(a => a.LoginName.Contains(UserName));
                }
                if (DepartmentId!=-1)
                {
                    data = data.Where(a => a.DepartmentId== DepartmentId);
                }
                PageDTO<User_PageDTO> page = new PageDTO<User_PageDTO>
                {
                    recordCount = data.Count(),
                    plist = data.OrderBy(a => a.Id).Skip((Pageindex - 1) * Pagesize).Take(Pagesize)
                };

                return new ResultDTO<PageDTO<User_PageDTO>>
                {
                    Code = Domain.Enum.ResultStatusCode.SUCCESS,
                    Data = page,
                    Message = "请求成功"
                };
            }
            catch (Exception)
            {
                return new ResultDTO<PageDTO<User_PageDTO>>
                {
                    Code = Domain.Enum.ResultStatusCode.HANDLEFAIL,
                    Data = null,
                    Message = "请求失败"
                };
            }

        }

        /// <summary>
        /// 添加用户信息及角色信息(管理员可用)
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<ResultDTO<string>> AddUser(User_AddDTO entity)
        {
            //开启事务
            var tran = unitOfWork.GetDbContext().Database.BeginTransaction();
            try
            {
                //用户名判重、非空，角色信息（RoleId）合理判断，手机号、邮箱格式正确


                var lpassword = entity.UserInfo.Password;
                //先添加用户表信息
                entity.UserInfo.Password = entity.UserInfo.Password.GetMD5();//用户密码加密
                await userRepository.Insert(entity.UserInfo);
                await unitOfWork.SaveChanges();
                //再添加用户角色表信息
                List<RoleUsers> roleUsers = new List<RoleUsers>();
                entity.RoleId.ForEach(x =>
                {
                    roleUsers.Add(new RoleUsers() { UserId = entity.UserInfo.Id, RoleId = x });
                });
                await roleUsersRepository.InsertBatch(roleUsers);
                await unitOfWork.SaveChanges();
                //提交事务
                tran.Commit();
                return new ResultDTO<string>
                {
                    Code = Domain.Enum.ResultStatusCode.SUCCESS,
                    Message = "添加成功",
                    Data = "用户名：" + entity.UserInfo.LoginName + " 密码：" + lpassword
                };
            }
            catch (Exception)
            {
                //出错回滚事务
                tran.Rollback();
                return new ResultDTO<string>
                {
                    Code = Domain.Enum.ResultStatusCode.HANDLEFAIL,
                    Message = "添加失败",
                    Data = ""
                };
            }
        }
        /// <summary>
        /// 逻辑删用户信息
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public async Task<ResultDTO<string>> DeleteUser(string Ids)
        {
            //开启事务
            var tran = unitOfWork.GetDbContext().Database.BeginTransaction();
            try
            {
                string[] sids = Ids.Split(',');
                foreach (var item in sids)
                {
                    var data = userRepository.GetAll().Result.Where(x => x.Id == Convert.ToInt32(item)).FirstOrDefault();
                    data.IsDeleted = true;
                    await userRepository.Update(data);
                }
                await unitOfWork.SaveChanges();
                //提交事务
                tran.Commit();
                return new ResultDTO<string>
                {
                    Code = Domain.Enum.ResultStatusCode.SUCCESS,
                    Message = "删除成功",
                    Data = ""
                };
            }
            catch (Exception)
            {
                //回滚事务
                tran.Rollback();
                return new ResultDTO<string>
                {
                    Code = Domain.Enum.ResultStatusCode.SUCCESS,
                    Message = "删除失败",
                    Data = ""
                };
            }
        }

        /// <summary>
        /// 修改用户信息
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<ResultDTO<string>> UpdateUser(UserEditDTO entity)
        {
            using (var tran = await unitOfWork.GetDbContext().Database.BeginTransactionAsync())
            {
                try
                {
                    //根据用户名查询用户信息
                    UserInfo user = await userRepository.GetUserbyLoginName(entity.LoginName);
                    var text = user.Password;

                    //判断该用户是否为空以及该用户
                    if (user != null)
                    {
                        //判断ID是否一致
                        if (user.Id != entity.Id)
                        {
                            return new ResultDTO<string>(null, Domain.Enum.ResultStatusCode.HANDLEFAIL, "用户名已存在");
                        }
                    }

                    user = mapper.Map<UserInfo>(entity);
                    user.Password = text;

                    //转成用户表需要的格式并提交，
                    await userRepository.Update(user);

                    //查询该用户原本的角色信息
                    var roles = (await roleUsersRepository.GetAll()).Where(m => m.UserId == entity.Id).ToList();

                    //删除该用户原本的角色信息
                    roles.ForEach(async e =>
                    {
                        await roleUsersRepository.Delete(e);
                    });

                    //循环添加用户-角色表信息
                    foreach (var item in entity.Roles.Distinct())
                    {
                        var temp = new RoleUsers { RoleId = item, UserId = entity.Id };
                        await roleUsersRepository.Insert(temp);
                    }
                    //保存
                    int h = await unitOfWork.SaveChanges();

                    await tran.CommitAsync();
                    //返回
                    return new ResultDTO<string>("1", Domain.Enum.ResultStatusCode.SUCCESS, "修改成功");

                }
                catch (Exception ex)
                {
                    await tran.RollbackAsync();
                    return new ResultDTO<string>
                    {
                        Code = Domain.Enum.ResultStatusCode.HANDLEFAIL,
                        Message = "修改失败",
                        Data = ex.Message
                    };
                }
            }
        }
        /// <summary>
        /// 给用户分配角色
        /// </summary>
        /// <param name="RoleId">分配的角色ID</param>
        /// <param name="UserId">用户ID</param>
        /// <returns></returns>
        public async Task<ResultDTO<string>> AllocationRole(AllocationRoleDTO dto)
        {
            //开启事务
            var tran = unitOfWork.GetDbContext().Database.BeginTransaction();
            try
            {
                //删除用户的所有角色
                var data = roleUsersRepository.GetAll().Result.Where(a => a.UserId == dto.UserId).ToList();
                roleUsersRepository.DeleteBatch(data);
                unitOfWork.SaveChanges();
                //重新给用户添加角色
                List<RoleUsers> list = new List<RoleUsers>();
                foreach (var item in dto.RoleId)
                {
                    list.Add(new RoleUsers { RoleId = item, UserId = dto.UserId });
                }
                roleUsersRepository.InsertBatch(list);
                unitOfWork.SaveChanges();
                //提交事务
                tran.Commit();
                return new ResultDTO<string>
                {
                    Code = Domain.Enum.ResultStatusCode.SUCCESS,
                    Message = "操作成功",
                    Data = ""
                };
            }
            catch (Exception)
            {
                tran.Rollback();
                return new ResultDTO<string>
                {
                    Code = Domain.Enum.ResultStatusCode.HANDLEFAIL,
                    Message = "操作失败",
                    Data = ""
                };
            }


        }
        /// <summary>
        /// 刷新令牌
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<ResultDTO<LoginSuccessResponseDTO>> Refresh(string token)
        {
            try
            {
                string secret = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.IssuerSigningKey)), SecurityAlgorithms.HmacSha256).ToString();//秘钥 DOTO:从配置文件拿

                #region 解密token
                //IJsonSerializer serializer = new JsonNetSerializer();
                //IDateTimeProvider provider = new UtcDateTimeProvider();
                //IJwtValidator validator = new JwtValidator(serializer, provider);
                //IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
                //IJwtAlgorithm alg = new HMACSHA256Algorithm();
                //IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, alg);
                //var json = decoder.Decode(token, secret, true);
                #endregion
                //校验通过，返回解密后的字符串

                //获取负载信息
                var handler = new JwtSecurityTokenHandler();
                var payload = handler.ReadJwtToken(token).Payload;
                var claims = payload.Claims;
                //获取登录名
                var LoginName = claims.First(claim => claim.Type == "LoginName").Value;
                if (token != redis.Get(LoginName))
                {
                    //表示验证不通过
                    return new ResultDTO<LoginSuccessResponseDTO>(
                        null,
                        Domain.Enum.ResultStatusCode.PARAMSERROR,
                        "令牌有误"
                        );
                }
                else
                {
                    //获取用户信息
                    var user = await userRepository.GetUserbyLoginName(LoginName);
                    //自定义荷载信息
                    var clims = new List<Claim>();
                    clims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
                    clims.Add(new Claim("Id", user.Id.ToString()));
                    clims.Add(new Claim("LoginName", user.LoginName));
                    clims.Add(new Claim("Phone", user.Phone == null ? "空数据" : user.Phone));
                    clims.Add(new Claim("Email", user.Email == null ? "空数据" : user.Email));
                    //登录成功返回的DTO
                    LoginSuccessResponseDTO info = mapper.Map<LoginSuccessResponseDTO>(user);
                    //jwt令牌
                    info.TokenInfo = jwtHelper.CreateTokenString(clims);
                    //把令牌放入Redis，并添加过期时间(15分钟) 
                    redis.Set(user.LoginName, info.TokenInfo.TokenStr);
                    redis.Expire(user.LoginName, 60 * 90);
                    //刷新成功
                    return new ResultDTO<LoginSuccessResponseDTO>(
                        info,
                        Domain.Enum.ResultStatusCode.SUCCESS,
                        "刷新成功！！！"
                        );
                }

            }
            catch (TokenExpiredException)
            {
                //表示过期
                //表示验证不通过
                return new ResultDTO<LoginSuccessResponseDTO>(
                    null,
                    Domain.Enum.ResultStatusCode.HANDLEFAIL,
                    "令牌过期"
                    );
            }
            catch (SignatureVerificationException)
            {
                //表示验证不通过
                return new ResultDTO<LoginSuccessResponseDTO>(
                    null,
                    Domain.Enum.ResultStatusCode.HANDLEFAIL,
                    "令牌有误"
                    );
            }
            catch (Exception)
            {
                return new ResultDTO<LoginSuccessResponseDTO>(
                   null,
                   Domain.Enum.ResultStatusCode.HANDLEFAIL,
                   "发生错误"
                   );
            }
        }
    }
}
